#ifndef __PB_DISPATCHER_H__
#define __PB_DISPATCHER_H__

#include <map>
#include <google/protobuf/message.h>
#include "Timestamp.h"
#include "Connection.h"

using google::protobuf::Message;

namespace dsth {

class Callback {
public:
    virtual ~Callback() {};
    virtual void OnMessage(const Connection* conn,
        const Message* msg,
        Timestamp) const = 0;
};

template <typename T>
class CallbackT : public Callback {
public:
    typedef Acf::Delegate<void (const Connection*, const T*, Timestamp)> MessageCallback;
    CallbackT(const MessageCallback& callback)
        : callback_(callback) {
    }

    virtual void OnMessage(const Connection* conn,
        const Message* msg,
        Timestamp receiveTime) const {
            const T* concrete = dynamic_cast<const T*>(msg);
            callback_(conn, concrete, receiveTime);
    }

private:
    MessageCallback callback_;
};

class PBDispatcher {
public:
    PBDispatcher() {} // TODO: default callback
    ~PBDispatcher() {
        for (CallBackMap::iterator it = callbacks_.begin();
            it != callbacks_.end(); ++it) {
                delete it->second;
        }
    }
    void OnMessage(const Connection* conn, const Message* msg, Timestamp time) const {
        CallBackMap::const_iterator it = callbacks_.find(msg->GetDescriptor());
        if (it != callbacks_.end()) {
            it->second->OnMessage(conn, msg, time);
        }
        else {
            //TODO: default
        }
    }
    template<typename T>
    void RegisterMessageCallback(const typename CallbackT<T>::MessageCallback& callback) {
        CallbackT<T>* cb = new CallbackT<T>(callback);
        callbacks_[T::descriptor()] = cb;
    }

private:
    PBDispatcher(const PBDispatcher&);
    void operator=(const PBDispatcher&);

    typedef std::map<const google::protobuf::Descriptor*, Callback*> CallBackMap;
    CallBackMap callbacks_;
};

} // namespace
#endif // __PB_DISPATCHER_H__
